﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using Infrastructure;
using IvanAkcheurov.NTextCat.Lib.Legacy;
using SubsDownloader.Core.AppSettings;
using SubsDownloader.Core.EventArgs;

namespace SubsDownloader.Core.Entities
{
    public class TitleFileInfo
    {
        #region Data Members

        private string _filePath;
        private bool _hasSubs;
        private eStatus _status;
        private string _titleName;
        private string _season;
        private string _episode;

        #endregion

        public event EventHandler ItemChanged;   

        public TitleFileInfo TitleFolderInfo { get; set; }

        public eTitleType Type { get { return IsSeries ? eTitleType.Series : eTitleType.Movie; } }

        /// <summary>
        /// File name without the extention
        /// </summary>
        public string FileNameNoExt { get; set; }

        /// <summary>
        /// File folder name
        /// </summary>
        public string FolderName { get; set; }

        /// <summary>
        /// File full path
        /// </summary>
        public string FilePath
        {
            get { return _filePath; }
        }

        /// <summary>
        /// Folder full path
        /// </summary>
        public string FolderPath { get; set; }

        /// <summary>
        /// Indicate wheater tiltle is series or not
        /// </summary>
        public bool IsSeries { get; set; }

        /// <summary>
        /// Indicate wheater tiltle has two CD's
        /// </summary>
        public bool IsTwoCD { get; set; }

        /// <summary>
        /// CD number
        /// </summary>        
        public int CD { get; set; }

        public double Progress { get; set; }

        /// <summary>
        /// Title name
        /// </summary>        
        public string TitleName
        {
            get { return _titleName; }
            set
            {
                _titleName = value;
                InvokeItemChanged();
            }
        }

        /// <summary>
        /// Title year
        /// </summary>       
        public string Year { get; set; }

        /// <summary>
        /// Season numbert as string. only if title is series 
        /// </summary>        
        public string Season
        {
            get { return _season; }
            set
            {
                if (value.Length == 1 && !value.StartsWith( "0" ))
                    value = string.Concat( "0", value );

                _season = value;
            }
        }
 
        /// <summary>
        /// Episode numbert as string. only if title is series 
        /// </summary>        
        public string Episode
        {
            get { return _episode; }
            set
            {
                if (value.Length == 1 && !value.StartsWith( "0" ))
                    value = string.Concat( "0", value );
                _episode = value;
            }
        }

        /// <summary>
        /// File size
        /// </summary>        
        public ulong Size { get; set; }

        /// <summary>
        /// Indicate wheater media file is High Definition file
        /// </summary>
        public bool IsHD { get; set; }

        /// <summary>
        /// Indicate wheater media file is Full HD file. only if file is HD
        /// </summary>
        public bool? FullHD { get; set; }

        /// <summary>
        /// Title already have subtitles
        /// </summary>
        public bool HasSubs
        {
            get { return _hasSubs; }
            internal set
            {
                _hasSubs = value;
                Progress = 100;
                if (TitleFolderInfo != null)
                {
                    TitleFolderInfo.HasSubs = true;
                }
                InvokeItemChanged();
            }
        }

        public string SubsLanguage { get; set; }

        /// <summary>
        /// Show status for current item
        /// </summary>
        public eStatus Status
        {
            get
            {
                if (HasSubs)
                    return eStatus.Sucess;

                return _status;
            }
            internal set
            {
                _status = value;
                InvokeItemChanged();
            }
        }

        public List<LogRow> Logger { get; internal set; }

        protected TitleFileInfo ()
        {
            Status = eStatus.New;
            Logger = new List<LogRow>();
        }

        public TitleFileInfo ( string filePath )
            : this()
        {
            _filePath = filePath;
            if (filePath == null)
                throw new ArgumentNullException( "filePath" );

            if (!File.Exists( filePath ))
                throw new FileNotFoundException( "Coludnt find file", filePath );

            ScanFile( this, filePath, true );
            TitleFolderInfo = new TitleFileInfo();

            ScanFile( TitleFolderInfo, filePath, false );
            TitleFolderInfo._filePath = filePath;
        }

        public override string ToString ()
        {
            string retVal;
            if (IsSeries)
            {
                retVal = string.Format( "{0} S{1} E{2}", TitleName, Season, Episode );
            }
            else
            {
                retVal = !string.IsNullOrEmpty( Year ) ? string.Format( "{0} {1}", TitleName, Year ) : string.Format( "{0}", TitleName );
            }
            return retVal;
        }

        public string ToString ( string format )
        {
            string retVal = string.Empty;
            switch (format)
            {
                case "S":
                    if (IsSeries)
                    {
                        retVal = string.Format( "{0} S{1}", TitleName, Season );
                    }
                    break;
                case "E":
                    if (IsSeries)
                    {
                        retVal = string.Format( "{0} S{1} E{2}", TitleName, Season, Episode );
                    }
                    break;
            }

            return retVal;
        }

        #region Private Methods

        private void ScanFile ( TitleFileInfo titleFileInfo, string filePath, bool parseByFileName )
        {
            var fileInfo = new FileInfo( filePath );
            var subtitlesSettings = SettingsManager.Instane.GetSettings<SubtitlesSettings>();

            titleFileInfo.FileNameNoExt = Path.GetFileNameWithoutExtension( filePath );
            titleFileInfo.FolderPath = fileInfo.DirectoryName;

            if (fileInfo.Directory != null)
                titleFileInfo.FolderName = fileInfo.Directory.Name;


            string parseString = parseByFileName ? FileNameNoExt : FolderName;

            if (ParseIfTV( parseString ))
            {
                titleFileInfo.IsSeries = true;
                subtitlesSettings.Expressions.Sort( new Comparison<Expression>( CompareExprossions ) );
                foreach (var expression in subtitlesSettings.Expressions)
                {
                    var regex = new Regex( expression.Regex, RegexOptions.Compiled | RegexOptions.IgnoreCase );
                    var match = regex.Match( fileInfo.Name );
                    if (match.Success)
                    {
                        titleFileInfo.TitleName = match.Groups["series"].Value;
                        titleFileInfo.TitleName = UppercaseFirst( titleFileInfo.TitleName.Replace( ".", " " ).Trim() );
                        titleFileInfo.Season = match.Groups["season"].Value;
                        titleFileInfo.Episode = match.Groups["episode"].Value;
                        break;
                    }
                }
            }
            else
            {
                titleFileInfo.TitleName = ParseMovieName( parseString );
                titleFileInfo.Year = ParseYear( parseString );
            }


            ParseHD( titleFileInfo, parseString );

            string fileNameWithoutExtension = Path.GetFileNameWithoutExtension( fileInfo.Name );
            foreach (var subtitlesType in subtitlesSettings.SubtitlesTypes)
            {
                if (fileInfo.Directory != null)
                {
                    var subs = fileInfo.Directory.GetFiles( string.Concat( fileNameWithoutExtension, subtitlesType ) );
                    if (!titleFileInfo.HasSubs)
                    {
                        titleFileInfo.HasSubs = subs.Length > 0;
                        break;
                    }
                    else
                    { }
                }
            }
        }

        private void ParseHD ( TitleFileInfo titleFileInfo, string parseString )
        {
            var is720 = parseString.ToLower().Contains( "720p" );
            var is1080 = parseString.ToLower().Contains( "1080p" );
            titleFileInfo.IsHD = is720 || is1080 ? true : false;
            titleFileInfo.FullHD = titleFileInfo.IsHD && is1080 ? true : (bool?)null;
        }

        private string ParseYear ( string stringToParse )
        {
            Match year = Regex.Match( stringToParse, "(?<=.)\\d\\d\\d\\d\\b" );
            return year.Value;
        }

        private bool ParseIfTV ( string stringToParse )
        {
            Match isTv = Regex.Match( stringToParse, @".*(?i)s\d{1,2}e\d{1,2}.*|.*(?i)s\d{1,2}.e\d{1,2}.*|\d{1,2}x\d{1,2}|.*(?i)s\d{1,2} e\d{1,2}.*" );
            if (!isTv.Success)
            {
                isTv = Regex.Match( stringToParse, @".*part\d{1,2}.*|pt\.i\.|pt\.ii\.|pt\.ii\.", RegexOptions.IgnoreCase );
            }
            return isTv.Success;
        }

        private string ParseMovieName ( string stringToParse )
        {
            string movieName = Regex.Replace( stringToParse, "'", "" );
            string clean = "576p|720p|1080p|1080i|dircut|directors cut|dvdrip|dvdscreener|dvdscr|avchd|wmv|ntsc|pal|mpeg|ctu|dsr|hdtv|hd|r5|dvd|dvdr|dvd5|dvd9|bd5|bd9|dts|ac3|bluray|blu-ray|pdtv|stv|hddvd|xvid|divx|x264|dxva|(?-i)FESTIVAL|LIMITED|WS|FS|PROPER|REPACK|RERIP|REAL|RETAIL|EXTENDED|REMASTERED|UNRATED|CHRONO|THEATRICAL|DC|SE|UNCUT|INTERNAL|DUBBED|SUBBED|part\\d\\d";
            movieName = Regex.Replace( movieName, clean, "" );

            Match match = Regex.Match( movieName, @"(?i-:(?<MediaName>.*?)(\.){0,1}S(\d){1,2}(\.){0,1}E(\d){1,2})", RegexOptions.IgnoreCase );
            if (match.Success)
            {
                movieName = match.Groups["MediaName"].Value;
            }
            else
            {
                string movieRexEx = @"^(?i-:(?<MediaName>.*?)(\.| )(576p|720p|1080p|1080i|dircut|directors cut|dvdrip|dvdscreener|dvdscr|avchd|wmv|ntsc|pal|mpeg|dsr|hd|r5|dvd|dvdr|dvd5|dvd9|bd5|bd9|dts|ac3|bluray|blu-ray|hdtv|pdtv|stv|hddvd|xvid|divx|x264|dxva|(?-i)FESTIVAL|LIMITED|WS|FS|PROPER|REPACK|RERIP|REAL|RETAIL|EXTENDED|REMASTERED|UNRATED|CHRONO|THEATRICAL|DC|SE|UNCUT|INTERNAL|DUBBED|SUBBED|\d\d\d))";
                match = Regex.Match( movieName, movieRexEx, RegexOptions.IgnoreCase );
                if (match.Success)
                {
                    movieName = match.Groups["MediaName"].Value;
                }
            }
            movieName = Regex.Replace( movieName, "\\.|,|-|_|\\(|\\)|:|&", " " );
            movieName = UppercaseFirst( movieName );
            return movieName.Trim();
        }

        static string UppercaseFirst ( string text )
        {
            var split = text.ToLower().Split( ' ' );

            var resut = new StringBuilder();

            foreach (var s in split)
            {
                if (s.Length > 0)
                {
                    resut.AppendFormat( "{0} ", char.ToUpper( s[0] ) + s.Substring( 1 ) );
                }
            }
            resut.Length = resut.Length - 1;
            return resut.ToString();
        }

        private static int CompareExprossions ( Expression x, Expression y )
        {
            return x.Index.CompareTo( y.Index );
        }

        private void InvokeItemChanged ( )
        {
            EventHandler handler = ItemChanged;
            if (handler != null) handler( this, new System.EventArgs() );
        }
        #endregion

        internal void ReportProgress ( object sender, SubsProgressEventArgs e )
        {
            Logger.Add( new LogRow( e.UserState.ToString() ) );
            Progress = e.ProgressPercentage;
        }
    }

    public enum eTitleType
    {
        Series,
        Movie
    }
}